<?php
/**
 * $Id$
 */

/**
 * 插件处理类
 *
 * @package Plugin
 */
class Tinyask_Plugin
{
	/**
	 * 所有激活的插件
	 *
	 * @var array
	 */
	private static $_plugins = array();

	/**
	 * 已经加载的插件
	 *
	 * @var array
	 */
	private static $_required = array();

	/**
	 * 实例化的插件对象
	 *
	 * @var array
	 */
	private static $_instances = array();

	/**
	 * 临时存储变量
	 *
	 * @var array
	 */
	private static $_tmp = array();

	/**
	 * 唯一句柄
	 *
	 * @var string
	 */
	private $_handle;

	/**
	 * 组件
	 *
	 * @var string
	 */
	private $_component;

	/**
	 * 是否触发插件的信号
	 *
	 * @var boolean
	 */
	private $_signal;

	public function __construct($handle)
	{
		$this->_handle = $handle;
	}

	/**
	 * 插件初始化
	 *
	 * @param array $plugins
	 */
	public static function init(array $plugins)
	{
		$plugins['activated'] = array_key_exists('activated', $plugins) ? $plugins['activated'] : array();
		$plugins['handles'] = array_key_exists('handles', $plugins) ? $plugins['handles'] : array();

		self::$_plugins = $plugins;
	}

	/**
	 * 工厂方法实例化插件
	 *
	 * @param string $handle
	 * @return Tinyask_Plugin
	 */
	public static function factory($handle)
	{
		return isset(self::$_instances[$handle]) ? self::$_instances[$handle] :
		(self::$_instances[$handle] = new Tinyask_Plugin($handle));
	}

	/**
	 * 激活插件
	 *
	 * @param string $pluginName
	 */
	public static function activate($pluginName)
	{
		self::$_plugins['activated'][$pluginName] = self::$_tmp;
		self::$_tmp = array();
	}

	/**
	 * 禁用插件
	 *
	 * @param string $pluginName
	 */
	public static function deactivate($pluginName)
	{
		/** 去掉所有相关回调函数 */
		if(isset(self::$_plugins['activated'][$pluginName]['handles']) && is_array(self::$_plugins['activated'][$pluginName]['handles'])) {
			foreach (self::$_plugins['activated'][$pluginName]['handles'] as $handle => $handles) {
				self::$_plugins['handles'] = array_diff(self::$_plugins['handles'][$handle], $handles);
				if(empty(self::$_plugins['handles'][$handle])) {
					unset(self::$_plugins['handles'][$handle]);
				}
			}
		}

		/** 禁用当前插件 */
		unset(self::$_plugins['activated'][$pluginName]);
	}

	/**
	 * 导出当前插件设置
	 *
	 * @return array
	 */
	public static function export()
	{
		return self::$_plugins;
	}

	/**
	 * 获取插件文件的头信息
	 *
	 * @param string $pluginFile 插件文件
	 * @return array
	 */
	public static function parseInfo($pluginFile)
	{
		$tokens = token_get_all(file_get_contents($pluginFile));
		$isDoc = $isFunction = $isClass = $isInClass = $isInFunction = $isDefined = false;
		$current = null;

		$info = array(
			'description' => '',
			'title' => '',
			'author' => '',
			'homepage' => '',
			'version' => '',
			'activate' => false,
			'deactivate' => false,
			'config' => false,
			'personConfig' => false
		);

		$map = array(
			'package' => 'title',
			'author' => 'author',
			'link' => 'homepage',
			'version' => 'version'
		);

		foreach ($tokens as $token) {
			if(!$isDoc && is_array($token) && T_DOC_COMMENT == $token[0]) {
				$described = false;
				$lines = preg_split("\n", $token[1]);
				foreach ($lines as $line) {
					$line = trim($line);
					if(!empty($line) && $line[0] == '*') {
						$line = trim(substr($line, 1));
						if(!$described && !empty($line) && $line[0] == '@') {
							$described = true;
						}

						if(!$described && !empty($line)) {
							$info['description'] .= $line . "\n";
						} elseif ($described && !empty($line) && $line[0] == '@') {
							$info['description'] = trim($info['description']);
							$line = trim(substr($line, 1));
							$args = explode(' ', $line);
							$key = array_shift($args);

							if(isset($map[$key])) {
								$info[$map[$key]] = trim(implode(' ', $args));
							}
						}
					}
				}

				$isDoc = true;
			}

			if(is_array($token)) {
				switch ($token[0]) {
					case T_FUNCTION:
						$isFunction = true;
						break;
					case T_IMPLEMENTS:
						$isClass = true;
						break;
					case T_WHITESPACE:
					case T_COMMENT:
					case T_DOC_COMMENT:
						break;
					case T_STRING:
						$string = strtolower($token[0]);
						switch ($string) {
							case 'tinyask_plugin_interface':
								$isInClass = $isClass;
								break;
							case 'activate':
							case 'deactivate':
							case 'config':
							case 'personalConfig':
								if($isFunction) {
									$current = $string;
								}
								break;
							default:
								if(!empty($current) && $isInFunction && $isInClass) {
									$info[$current] = true;
								}
								break;
						}
						break;
						default:
							if(!empty($current) && $isInFunction && $isInClass) {
								$info[$current] = true;
							}
							break;
				}
			} else {
				$token = strtolower($token);
				switch ($token) {
					case '{':
						if($isDefined) {
							$isInFunction = true;
						}
						break;
					case '(':
						if($isFunction && !$isDefined) {
							$isDefined = true;
						}
						break;
					case '}':
					case ';':
						$isDefined = false;
						$isFunction = false;
						$isInFunction = false;
						$current = null;
						break;

					default:
						if(!empty($current) && $isFunction && $isInClass) {
							$info[$current] = true;
						}
						break;
				}
			}
		}

		return $info;
	}

	/**
	 * 获取插件路径和类名
	 * 返回一个数组
	 * 第一项为插件路径
	 * 第二项为类名
	 *
	 * @param string $pluginName
	 * @param string $path
	 * @return array
	 */
	public static function portal($pluginName, $path)
	{
		switch (true) {
			case is_file($pluginName = $path . '/' . $pluginName . '/plugin.php'):
				$className = $pluginName . '_Plugin';
				break;
			case is_file($pluginName = $path . '/' . $pluginName . '.php'):
				$className = $pluginName;
				break;
			default:
				throw new Tinyask_Exception('Missing Plugin ' . $pluginName, 404);
		}

		return array($pluginName, $className);
	}

	/**
	 * 插件调用后的触发器
	 *
	 * @param boolean $signal 触发器
	 * @return Tinyask_Plugin
	 */
	public function trigger(&$signal)
	{
		$signal = false;
		$this->_signal = &$signal;
		return $this;
	}

	/**
	 * 判断插件是否存在
	 *
	 * @param string $pluginName
	 * @return mixed
	 */
	public function exists($pluginName)
	{
		return array_search($pluginName, self::$_plugins['activated']);
	}

	/**
	 * 设置回调函数
	 *
	 * @param string $component 当前组件
	 * @param mixed $value 回调函数
	 */
	public function __set($component, $value)
	{
		$component = $this->_handle . ':' . $component;
		self::$_plugins['handles'][$component][] = $value;
		self::$_tmp['handles'][$component][] = $value;
	}

	/**
	 * 通过魔术函数设置当前组件位置
	 *
	 * @param string $component 当前组件
	 * @return Tinyask_Plugin
	 */
	public function __get($component)
	{
		$this->_component = $component;
		return $this;
	}

	/**
	 * 回调处理函数
	 *
	 * @param string $component 当前组件
	 * @param array $args
	 * @return mixed
	 */
	public function __call($component, $args)
	{
		$component = $this->_handle . ':' . $component;
		$last = count($args);
		$args[$last] = $last > 0 ? $args[0] : false;

		if(isset(self::$_plugins['handles'][$component])) {
			$args[$last] = null;
			$this->_signal = true;
			foreach (self::$_plugins['handles'][$component] as $callback) {
				$args[$last] = call_user_func_array($callback, $args);
			}
		}

		return $args[$last];
	}
}
